int DOUBLECLICK = 300;  //number of milliseconds
final int RECORD = 0;
final int EDIT = 1;
final int PLAY = 2;

//for now, I'm throwing all of the tabs into MainLayer...although it seems like it might
//be a little more efficient to abstract out the stuff that all three tabs need and create
//a tab class (which extends Layer) and then implement each as a separate class...
class MainLayer extends Layer
{
  int[] ICONORDER = new int[] {5,0,1,3};

  String[] TABS = {"Record","Edit","Play"};
  int[] TABVAL = {RECORD,EDIT,PLAY};
  int[] TABSTART = new int[TABS.length + 1];  //one extra for the last one's endpoint
  int ACTIVETAB = EDIT;
     
  //myLayers should probably be renamed editLayers...
  Layer[] myLayers;  //for generic function calls (mDraw,mDown, etc...)
  
  //edit layer
  Bin bin;
  Timeline timeline;
  Layer properties;
  
  //record layer
  recordLayer record;
  
  //play layer
  playLayer play;
  
  boolean GLOBALDRAG = false;
  dragFile DRAGFILE = null;
  
  MainLayer()
  {
    super(0,0,width,height,0);
    newProject();
  }

  boolean mDraw()
  {
    //update the sublayers first
    for(int i = 0; i < myLayers.length; i++)
    {
      myLayers[i].mDraw();
    }
    
    pg.beginDraw();
    pg.background(BORDERCOLOR);
    pg.fill(FILLCOLOR);
    pg.noStroke();
    pg.rect(3,3,pg.width-6,pg.height-6);
    for(int i = 0; i < myLayers.length; i++)
    {    
      pg.image(myLayers[i].pg,myLayers[i].x,myLayers[i].y);
    }
        
    //if no one is setting the cursor, return to the default cursor
    boolean resetCursor = true;
    for(int i = 0; i < myLayers.length; i++)
    {
      if(myLayers[i].mTest() > 0) resetCursor = false;
    }
    if(resetCursor) setCursor(new Cursor(0));
    
    
    if(ACTIVETAB == EDIT)
    {
      if(GLOBALDRAG){
        if(timeline.mTest() == 2 && !timeline.DODRAG) {
          timeline.DODRAG = true;
          timeline.DRAGINSTANCE = new dataInstance(DRAGFILE.whichFile);
        }
        pg.image(DRAGFILE.pg,mouseX-20,mouseY-20);
      }
    }else if(ACTIVETAB == PLAY){
      //I may not actually need to draw anything in here... the code above takes care of 
      //drawing whichever layers are contained in myLayers      
  }  
    
    
    drawUI();
    pg.endDraw();
    pg.modified = true;
    
    return DONE;
  }

  //this is perhaps messier than it should be (could use cleaning up/better commenting)
  void mMessage(Message m)
  {
    switch(m.target)
    {
    case 0:  //string, addFile
      if(m.data.indexOf(PROJECTNAME + ".txt") != -1)
      {
        popWarn("Cannot load project export into project file!",280);
        return;
      }
      if(m.type == 2) bin.addFile(m.data);
      break; 
    case 1:  //delete activeFile
      if(m.data.equals("true")){
        dataFile df1 = (dataFile)bin.data.get(bin.selected);
        timeline.purge(df1);
        bin.dropFile();
      }
      break;
    case 2:
      dataFile d = (dataFile)bin.data.get(bin.selected);
      dataFileProp newP = new dataFileProp(d,myLayers[1].x,myLayers[1].y,myLayers[1].w,myLayers[1].h);
      myLayers[1] = (Layer)newP;
      properties = (Layer)newP;
      break;
    case 3:
      dataFileProp p = (dataFileProp)myLayers[1];
      p.data.setName(m.data);
      p = new dataFileProp(p.data,p.x,p.y,p.w,p.h);
      myLayers[1] = (Layer)p;
      properties = (Layer)p;
      bin.populateFiles();
      break;
    case 4:
      timelineProp newT = new timelineProp(timeline,myLayers[1].x,myLayers[1].y,myLayers[1].w,myLayers[1].h);
      myLayers[1] = (Layer)newT;
      properties = (Layer)newT;
      break;
    case 5:
      timeline.setName(m.data);
      break;
    case 6:
      if(!m.data.equals("")) timeline.setDir(m.data);
      break;
    case 7:
      if(m.data.equals("")) return; //user canceled
      if(confirmInt(m.data) != 0) timeline.softSetEndTime(confirmInt(m.data));
      break;
    case 8:
      if(m.data.equals("true")) saveFile();
      break;
    case 9:
      load(m.data);
      break;
    case 10:
      GLOBALDRAG = true;
      dataFile dF = (dataFile)bin.data.get(Integer.parseInt(m.data));
      DRAGFILE = new dragFile(dF);
      break;
    case 11:
      if(m.data.equals("true")) timeline.setEndTime(timeline.requestTime);
      break;
    case 12:
      if(m.data.equals("")) return;
      if(confirmInt(m.data) != 0) timeline.vScale = confirmInt(m.data);
      break;      
    case 13:
      dataProp newD = new dataProp(timeline.mouseOver,myLayers[1].x,myLayers[1].y,myLayers[1].w,myLayers[1].h);
      myLayers[1] = (Layer)newD;
      properties = (Layer)newD;
      break;
    case 14:
      dataProp d14 = (dataProp)myLayers[1];
      d14.data.setName(m.data);
      break;
    case 15:
      dataProp p15 = (dataProp)myLayers[1];
      dataFileProp newP15 = new dataFileProp(p15.data.file,myLayers[1].x,myLayers[1].y,myLayers[1].w,myLayers[1].h);
      myLayers[1] = (Layer)newP15;
      properties = (Layer)newP15;
      break;      
    case 16:
      if(m.data.equals("")) return; //user canceled
      dataProp p16 = (dataProp)myLayers[1];
      int newDur = confirmInt(m.data);
      if(newDur <= 0)
      {
        popWarn("Duration must be positive!"); 
        return;
      }
      
      //first check to see if changing the size would cause this file to overlap another
      dataInstance d16 = p16.data.copy((int)p16.data.offset);
      d16.setDuration(newDur); 
      Track t16 = (Track)timeline.data.get(timeline.getTrackNum(p16.data));   
      if(timeline.testClear(d16,t16)) {
        p16.data.setDuration(newDur);
      }else{
        popWarn("Overlap error!");
      }
      break;      
    case 17:
      if(m.data.equals("")) return; //user canceled
      float newScale = confirmFloat(m.data);
      if(newScale <= 0.0) 
      {
        popWarn("Scale must be positive!"); 
        return;
      }
      //first check to see if changing the size would cause this file to overlap another
      dataProp p17 = (dataProp)myLayers[1];
      dataInstance d17 = p17.data.copy((int)p17.data.offset);
      d17.setScale(newScale); 
      Track t17 = (Track)timeline.data.get(timeline.getTrackNum(p17.data));
      if(timeline.testClear(d17,t17)) {
        p17.data.setScale(newScale);
      }else{
        popWarn("Overlap error!");
      }
      break;
    case 18:
      if(m.data.equals("")) return; //user canceled
      int newOff = confirmInt(m.data);
      dataProp p18 = (dataProp)myLayers[1];
      if(newOff < 0.0 || newOff > timeline.endTime - p18.data.getDuration()) 
      {
        popWarn("Off the timeline!"); 
        return;
      }
      //first check to see if changing the size would cause this file to overlap another
      dataInstance d18 = p18.data.copy((int)p18.data.offset);
      d18.offset = newOff;
      Track t18 = (Track)timeline.data.get(timeline.getTrackNum(p18.data));
      if(timeline.testClear(d18,t18)) {
        p18.data.offset = newOff;
      }else{
        popWarn("Overlap error!");
      }   
      break;
    case 19:
      if(m.data.equals("")) return; //user canceled
      int newIn = confirmInt(m.data);
      dataProp p19 = (dataProp)myLayers[1];
      if(newIn < 0.0)
      {
        popWarn("Minimum inPoint is zero!"); 
        newIn = 0;
      }
      if(newIn > p19.data.outPoint-1)
      {
        popWarn("inPoint must be smaller than outPoint!"); 
        newIn = p19.data.outPoint-1;
      }
      //first check to see if changing the size would cause this file to overlap another
      dataInstance d19 = p19.data.copy((int)p19.data.offset);
      int offOff = (int)((newIn - p19.data.inPoint)*p19.data.dataScale);
      d19.inPoint = (int)newIn;
      d19.offset += offOff;
      Track t19 = (Track)timeline.data.get(timeline.getTrackNum(p19.data));
      if(timeline.testClear(d19,t19)) {
        p19.data.inPoint = (int)newIn;
        p19.data.offset += offOff;
      }else{
        popWarn("Overlap error!");
      }
      break;    
    case 20:
      if(m.data.equals("")) return; //user canceled
      int newOut = confirmInt(m.data);
      dataProp p20 = (dataProp)myLayers[1];
      if(newOut < 1)
      {
        popWarn("Minimum outPoint is one!"); 
        newOut = 1;
      }
      if(newOut > p20.data.file.getDuration())
      {
        newOut = p20.data.file.getDuration();
        popWarn("Maximum outPoint for this " + p20.data.file.getFileName() + " is " + newOut + ".",280); 
      }
      //first check to see if changing the size would cause this file to overlap another
      dataInstance d20 = p20.data.copy((int)p20.data.offset);
      d20.outPoint = (int)newOut;
      Track t20 = (Track)timeline.data.get(timeline.getTrackNum(p20.data));
      if(timeline.testClear(d20,t20)) {
        p20.data.outPoint = (int)newOut;
      }else{
        popWarn("Overlap error!");
      }
      break; 
    case 21:
      int newFN = confirmInt(m.data);
      record.fileNum = newFN;      
      break;
    case 22:
      if(!m.data.equals("")) play.openFile(m.data);
      break;
    case 23:
       dataFileProp dfb = (dataFileProp)myLayers[1];
       ICONORDER = new int[] {13,14,15,16};
       myLayers = new Layer[1];
       myLayers[0] = (Layer)(new playLayer(dfb.data));
       break;
    case 24:
      int whichAction = confirmInt(m.data);
      if(whichAction == 0)
      {
        layers.add(new xbcPop(record.fileNum));
        ACTIVELAYER++;
      }
      if(whichAction == 1)
      {
        layers.add(new bodyRig(record.fileNum));
        ACTIVELAYER++;        
      }
      if(whichAction == 2)
      {
        layers.add(new gamepadPop(record.fileNum));
        ACTIVELAYER++;
      }
      if(whichAction == 3)
      {
        layers.add(new torqueBipedRig(record.fileNum));
        ACTIVELAYER++;
      }
      if(whichAction == 4)
      {
        layers.add(new torqueFaceRig(record.fileNum));
        ACTIVELAYER++;        
      }
      if(whichAction == 5)
      {
        layers.add(new customRig(record.fileNum));
        ACTIVELAYER++;
      }
      break;
    default:
      println("Unrecognized message!");
      break;
    }
  }

  void drawUI()
  {
   
    //this is the side menu, whose icons are determied by the values in ICONORDER
    //function calls are dependent on which icon was clicked on, not which position, so feel free
    //to change these around as needed
    pg.fill(BORDERCOLOR);
    pg.rect(43,0,2,height);
    pg.rect(43,43,width,2);
    //pg.rect(0,3+ICONORDER.length*40,45,2);

    pg.strokeWeight(1);
    
    for(int i = 0; i < ICONORDER.length; i++)
    {
      pg.stroke(BUTTONSTROKE);
      if(mouseX > x+3 && mouseX < x+43 && mouseY > y+3+40*i && mouseY < y+43+40*i)
      {
        if(mousePressed) pg.fill(BUTTONDOWN);
        else pg.fill(BUTTONOVER);
      }
      else{
        pg.fill(BUTTONFILL);
      }
      pg.rect(3,3+40*i,40,40);      
      pg.image(icons[ICONORDER[i]],7,8+40*i,32,32);
    }
    
    //this is the tabs code
    int padding = 8;
    pg.textFont(font,28);
    int offset = 44;
    for(int i = 0; i < TABS.length; i++)
    {
      TABSTART[i] = offset;
      pg.fill(FILLCOLOR);
      pg.stroke(BUTTONSTROKE);
      pg.rect(offset,3,pg.textWidth(TABS[i]) + 2*padding,40);
      offset += padding;
      pg.fill(TEXTCOLOR);
      pg.text(TABS[i],offset,36);
      offset += padding + pg.textWidth(TABS[i]);
      TABSTART[i+1] = offset;  //a bit repetitive, but it ensures that the last one gets set.
    }
  }

  boolean UIPress()
  {
    if(mouseX < x+43 || mouseY < y+43) return true;
    return false;
  }

  boolean UIRelease()
  {
    if(mouseX > x+3 && mouseX < x+43 && mouseY > y+3 && mouseY < y+3+ICONORDER.length*40){
      int whichButton = ICONORDER[(int)((mouseY-y-3)/40)];
      if(whichButton == 5) newProject();
      if(whichButton == 0) softSave();
      if(whichButton == 1) softLoad();
      if(whichButton == 3) export();     
      if(whichButton == 11)
      {
        layers.add(new xbcPop(record.fileNum));
        ACTIVELAYER++;
      }
      if(whichButton == 12)
      {
        layers.add(new bodyRig(record.fileNum));
        ACTIVELAYER++;
      }
      if(whichButton == 13)
      {
        layers.add(new FilePop(ACTIVELAYER,22,1));
        ACTIVELAYER++; 
      }
      
      if(whichButton == 14) play.play();
      if(whichButton == 15) play.pause();
      if(whichButton == 16) play.stop();
      if(whichButton == 17) play.seekLeft();
      if(whichButton == 18) play.seekRight();
      if(whichButton == 19) play.skipLeft();
      if(whichButton == 20) play.skipRight();
      
      if(whichButton == 21)
      {
        layers.add(new torqueFaceRig(record.fileNum)); //torqueBipedRig(record.fileNum));
        ACTIVELAYER++;
      }
      
    }
    if(mouseY > y+3 && mouseY < y+43 && mouseX > y+43 && mouseX < TABSTART[TABSTART.length-1])
    {
      int whichTab = -1;
      for(int  i = 0; i < TABS.length; i++)
      {
        if(mouseX-x > TABSTART[i] && mouseX-x < TABSTART[i+1]) whichTab = i;
      }
      ACTIVETAB = TABVAL[whichTab];
      switch(ACTIVETAB)
      {
        case RECORD:
          ICONORDER = new int[] {};
          myLayers = new Layer[1];
          myLayers[0] = (Layer)(record);
          break;
        case EDIT:
          ICONORDER = new int[] {5,0,1,3};
          myLayers = new Layer[3];
          myLayers[0] = (Layer)bin;
          myLayers[1] = (Layer)properties;
          myLayers[2] = (Layer)timeline;
          break;
        case PLAY:
          ICONORDER = new int[] {13,14,15,16};
          myLayers = new Layer[1];
          myLayers[0] = (Layer)(new playLayer());
          break;
        default:
          println("Don't recognize that tab!");
          break; 
      }
      println("Tab " + whichTab);
    }
    return false;
  }

  void openProp(Timeline t)
  {
    timelineProp newT = new timelineProp(t,myLayers[1].x,myLayers[1].y,myLayers[1].w,myLayers[1].h);
    myLayers[1] = (Layer)newT;
  }  


  //the basic idea with the exporter is that the timeline should look at all of its tracks, and each frame
  //query each one on that track's next keyframe...  whichever has the lowest (!= -1) gets to throw theirs 
  //on the pile...the end result being a well sorted ArrayList of strings ready for export...

  boolean export()
  {
    //first go through and remove any empty Tracks
    int pNumTracks = timeline.numTracks;
    timeline.cullEmptyTracks();
    
    //if there's nothing left...what are we exporting?!?
    if(timeline.numTracks == 0)
    {
      for(int i = 0; i < pNumTracks; i++)
      {
        timeline.data.add(new Track(timeline.endTime));
      }
      timeline.numTracks = pNumTracks;
      popWarn("Nothing to export!");
      return false;
    }
    
    //Okay, we've made it this far... grab the NumDigital and NumAnalog off each track
    String nD = "";
    String nA = "";
    for(int i = 0; i < timeline.numTracks; i++)
    {
      Track track = (Track)timeline.data.get(i);
      dataInstance di = (dataInstance)track.get(0);  //just use the first one and assume the rest... DANGEROUS
      nD += di.numDigital + " ";
      nA += di.numAnalog + " ";
    }
    
    //and now build a new arraylist of Data
    ArrayList export = new ArrayList();
    int[] Inst = new int[timeline.numTracks];    //which instance are we currently on?
    int[] Key = new int[timeline.numTracks];     //which key within that instance? 
    int[] Value = new int[timeline.numTracks];   //what is that key's value?
    dataInstance[] Data = new dataInstance[timeline.numTracks];  //easier to keep the dataInstance for that key
    Arrays.fill(Inst,0);  //all remaining tracks have at least one instance
    Arrays.fill(Key,0); //all instances have at least one key
    Arrays.fill(Value,0); //that key has a value
    
    //insert some leveled out files to start off each input...
    
    
    boolean keepGoing = true;
    while(keepGoing)
    {
      //update all the values...
      //keepGoing = false;  //assume we're done...
      println("-------------------------");
      for(int i = 0; i < timeline.numTracks; i++)
      {
        Track track = (Track)timeline.data.get(i);
        dataInstance d = (dataInstance)track.get(Inst[i]);  //get the currently-being-worked-on instance
        Data[i] = d;
        Value[i] = d.getKeyTime(Key[i]);
        if(Value[i] == -1 && track.size()-1 > Inst[i])
        {
          Key[i] = 0;
          d = (dataInstance)track.get(++Inst[i]);  //get the next instance
          Value[i] = d.getKeyTime(Key[i]); 
          Data[i] = d;
        }
        println("Track " + i + ":" + Value[i]);        
        //if(Value[i] != -1) keepGoing = true;  //keep going!
      } //end update values
      println("--------------------------");
      //now add the lowest to our data array and increment that Track's key value
      boolean[] whichTracks = getLowest(Value);
      keepGoing = false;
      for(int i = 0; i < timeline.numTracks; i++)
      {
        if(whichTracks[i])
        {
          keepGoing = true;
          export.add(i + " " + Data[i].getKeyData(Key[i]));
          Key[i]++;
        }
      }
    }//end while-loop
    
    //now SAVE the file!
    String[] txt = new String[export.size() + 3];
    txt[0] = timeline.numTracks + "";  //all standard input files have only one controller
    txt[1] = nD;
    txt[2] = nA;
    for(int i = 0; i < export.size(); i++)
    {
      txt[i+3] = (String)export.get(i);
      println(txt[i+3]);
    }
    //saveStrings...    
    String filename = PROJECTNAME + ".txt";
    //exports go in the projects directory
    File out = new File(System.getProperty("user.dir") + "\\projects\\" + filename);  
    try{
      FileOutputStream outStream = new FileOutputStream(out);
      saveStrings(outStream, txt); 
    }
    catch(FileNotFoundException e){
      popWarn("Save failed!");
    }
    
    export = new ArrayList();        
    
    //add back the tracks we deleted
    for(int i = 0; i < pNumTracks - timeline.numTracks; i++)
    {
      timeline.data.add(new Track(timeline.endTime));
    }
    timeline.numTracks = pNumTracks;
    return false;
  }
  
  //returns a boolean array of which tracks contain the lowest value (neccessary because different tracks
  //might have key values at the exact same millisecond)
  boolean[] getLowest(int[] d)
  {
    boolean[] r = new boolean[d.length];
    Arrays.fill(r,false);
    
    int lowest = 0;
    for(int i = 0; i < d.length; i++)
    {
      if(d[lowest] == -1 && d[i] != -1) lowest = i;
      if(d[i] != -1 && d[i] < d[lowest])
      {
        Arrays.fill(r,false);
        lowest = i;
        r[i] = true;
      }else if(d[i] != -1 && d[i] == d[lowest])
      {
        r[i] = true;
      }
    }
    return r;
  }

  void softSave()
  {
    String curDir = System.getProperty("user.dir");
    File test = new File(curDir + "\\" + timeline.saveDir + "\\" + PROJECTNAME + ".xml");
    //if the file exists, confirm overwrite
    if(test.isFile())
    {
      layers.add(new PopConfirm(ACTIVELAYER,8,"Overwrite " + PROJECTNAME + ".xml?",0));
      ACTIVELAYER++;
    }
    else{  //otherwise just save the sucker
      saveFile();  
    }
  }
  
  void newProject()
  {
    bin = new Bin();
    properties = new Layer(width-200,44,200,200,0);
    timeline = new Timeline();
    
    record = new recordLayer();
    
    play = new playLayer();

    myLayers = new Layer[3];
    myLayers[0] = (Layer)bin;
    myLayers[1] = (Layer)properties;
    myLayers[2] = (Layer)timeline;

    for(int i = 0; i < 3; i++)
    {
      myLayers[i].MINX=44;
      myLayers[i].MINY=44;
    }
    
    mDraw();
  }

  void saveFile()
  {
    //I couldn't get XMLElement's toString function to work properly, so I'm doing it the old fashioned way

    ArrayList data = new ArrayList();
    data.add("<?xml version=1.0>");  
    data.add("<project name='" + PROJECTNAME + "'"
      + " saveDir='" + timeline.saveDir + "'"
      + " endTime="  + timeline.endTime 
      + " numTracks=" + timeline.numTracks
      + " vScale=" + timeline.vScale
      + " hScale=" + timeline.hScale
      + " x=" + timeline.x + " y=" + timeline.y
      + " w=" + timeline.w + " h=" + timeline.h + ">");
    data.add("  <binData"
      + " x=" + bin.x + " y=" + bin.y
      + " w=" + bin.w + " h=" + bin.h + ">");
    int binCount = bin.data.size();
    for(int i = 0; i < binCount; i++)
    {
      dataFile df = (dataFile)bin.data.get(i);
      data.add("    <bin path='\\data\\" + df.getFileName() + "'>" + df.name + "</bin>");
    }
    data.add("  </binData>");
    data.add("  <timelineData>");
    for(int i = 0; i < timeline.numTracks; i++)
    {
      Track track = (Track)timeline.data.get(i);
      for(int j = 0; j < track.size(); j++)
      {
        dataInstance f = (dataInstance)track.get(j);
        data.add("    <instance track=" + i + " file=" + bin.getFileNum(f.file.getFileName())
              + " offset=" + f.offset
              + " dataScale=" + f.dataScale
              + " inPoint=" + f.inPoint
              + " outPoint=" + f.outPoint + ">" + f.name + "</instance>");
      }
    }   
    data.add("  </timelineData>");
    data.add("  <propData"
      + " x=" + myLayers[1].x + " y=" + myLayers[1].y
      + " w=" + myLayers[1].w + " h=" + myLayers[1].h + "></propData>");
    data.add("</project>"); 
    String[] rxml = new String[data.size()];
    for(int i = 0; i < data.size(); i++)
    {
      rxml[i] = (String)data.get(i);
      println(rxml[i]);
    }
    //saveStrings...    
    File out = new File(System.getProperty("user.dir") + "\\" + timeline.saveDir + "\\" + PROJECTNAME + ".xml");  
    try{
      FileOutputStream outStream = new FileOutputStream(out);
      saveStrings(outStream, rxml); 
    }
    catch(FileNotFoundException e){
      println("Whoops!");
    }

  }

  void softLoad()
  {
    layers.add(new FilePop(ACTIVELAYER,9,1));
    ACTIVELAYER++;
  }

  void load(String path)
  {
    File xmlF = new File(path);
    if(!xmlF.isFile()) return;  //better to put an alert
    XMLElement xml = null;
    try{
      FileInputStream xmlS = new FileInputStream(xmlF);
      xml = new XMLElement(xmlS);
    }
    catch(FileNotFoundException e) {
      println("FileNotFoundException!");
    }
    catch(IOException e){
      println("IOException!"); 
    }
    catch(XMLParseException e){
      popWarn("Not an XML file!");
    }
    if(xml != null)
    {
      newProject();
      
      PROJECTNAME = xml.getStringAttribute("name");
      timeline.saveDir = xml.getStringAttribute("saveDir");
      timeline.endTime = xml.getIntAttribute("endTime");
      timeline.setEndTime(timeline.endTime);
      timeline.numTracks = xml.getIntAttribute("numTracks");
      for(int i = 1; i < timeline.numTracks; i++){timeline.data.add(new Track(timeline.endTime));}
      timeline.vScale = xml.getFloatAttribute("vScale");
      timeline.hScale = xml.getFloatAttribute("hScale");
      timeline.x = xml.getIntAttribute("x");
      timeline.y = xml.getIntAttribute("y");
      timeline.w = xml.getIntAttribute("w");
      timeline.h = xml.getIntAttribute("h");

      //println(pn + " : " + pd + " : " + pl);
      XMLElement binX = xml.getChild(0);
      bin.clear(); //clear out all the currently loaded data points
      bin.x = binX.getIntAttribute("x");
      bin.y = binX.getIntAttribute("y");
      bin.w = binX.getIntAttribute("w");
      bin.h = binX.getIntAttribute("h");

      int binCount = binX.getChildCount();
      //println("binCount: " + binCount);
      for (int i = 0; i < binCount; i++) {
        XMLElement child = binX.getChild(i);
        String activepath = System.getProperty("user.dir");
        dataFile df = new dataFile(activepath + "\\" + child.getStringAttribute("path")); 
        if(df.isValid)
        {
          df.name = child.getContent();
          bin.data.add(df);
        }else{
          popWarn("Could not find " + df.getFileName());
        }
      }
      bin.populateFiles();
      
      XMLElement data = xml.getChild(1);
      int dataCount = data.getChildCount();
      for (int i = 0; i < dataCount; i++) {
        XMLElement child = data.getChild(i);
        Track track = (Track)timeline.data.get(child.getIntAttribute("track"));
        dataInstance dI = new dataInstance(bin.getFileByNum(child.getIntAttribute("file")));
        dI.name = child.getContent();
        dI.offset = child.getFloatAttribute("offset");
        dI.dataScale = child.getFloatAttribute("dataScale");
        dI.inPoint = child.getIntAttribute("inPoint");
        dI.outPoint = child.getIntAttribute("outPoint");        
        track.add(dI);
      }
      
      XMLElement prop = xml.getChild(2);
      myLayers[1].x = prop.getIntAttribute("x");
      myLayers[1].y = prop.getIntAttribute("y");
      myLayers[1].w = prop.getIntAttribute("w");
      myLayers[1].h = prop.getIntAttribute("h");
      
    }  
    xmlF = null;   
  }



  void mPress()
  {
    if(UIPress()) return;

    for(int i = 0; i < myLayers.length; i++)
    {
      if(mouseX > myLayers[i].x-3 && mouseX < myLayers[i].x+myLayers[i].w+3 &&
        mouseY > myLayers[i].y-3 && mouseY < myLayers[i].y+myLayers[i].h+3)
      {
        boolean allowPress = true;
        for(int j = i; j < myLayers.length;j++)
        {
          if(j > i && myLayers[j].mTest()==2) allowPress = false;
        }
        if(allowPress) myLayers[i].mPress();
      }
    }
  }

  void mRelease()
  {
   if(UIRelease()) return;

   if(ACTIVETAB == RECORD) mReleaseRec();   
   if(ACTIVETAB == EDIT) mReleaseEdit();
   if(ACTIVETAB == PLAY) mReleasePlay(); 
  }
  void mReleaseRec()
  {
    myLayers[0].mRelease();
  }
  
  void mReleaseEdit()
  {
    if(GLOBALDRAG)
    {
      GLOBALDRAG = false;
      if(timeline.mTest() == 2){ //timeline gets first dibs
        timeline.freezeDrag();
        return; 
      }
      //if not caught by the Timeline, turn off DODRAG & DRAGINSTANCE
      timeline.DODRAG = false;
      timeline.DRAGINSTANCE = null;
      
      if(myLayers[1].mTest() > 0){  //if the timeline didn't grab it, give it to the property viewer
        dataFileProp newP = new dataFileProp(DRAGFILE.whichFile,myLayers[1].x,myLayers[1].y,myLayers[1].w,myLayers[1].h);
        myLayers[1] = (Layer)newP;
        return;
      }
    }
    
    if(timeline.hSliderDrag || timeline.sliderDrag)
    {
      timeline.hSliderDrag = false;
      timeline.sliderDrag = false;
      //t.drawDisplay();
    }

    for(int i = 0; i < myLayers.length; i++)
    {  
      if(mouseX > myLayers[i].x-3 && mouseX < myLayers[i].x+myLayers[i].w+3 &&
         mouseY > myLayers[i].y-3 && mouseY < myLayers[i].y+myLayers[i].h+3)
      {
        boolean allowPress = true;
        for(int j = i; j < myLayers.length;j++)
        {
          if(j > i && myLayers[j].mTest()==2) allowPress = false;
        }
        if(allowPress) myLayers[i].mRelease();
      }
      myLayers[i].dragActive = false;
      Arrays.fill(myLayers[i].resizeActive,false);   
    }
  }
  
  void mReleasePlay()
  {
    for(int i = 0; i < myLayers.length; i++)
    {  
      if(mouseX > myLayers[i].x-3 && mouseX < myLayers[i].x+myLayers[i].w+3 &&
         mouseY > myLayers[i].y-3 && mouseY < myLayers[i].y+myLayers[i].h+3)
      {
        myLayers[i].mRelease();
      }
    }
  }


  void mDrag()
  {
    for(int i = 0; i < myLayers.length; i++){
      myLayers[i].mDrag();
    }
  }


  void mKeyDown()
  {
    for(int i = 0; i < myLayers.length; i++){
      myLayers[i].mKeyDown();
    }    
  }

  void mKeyUp()
  {
    for(int i = 0; i < myLayers.length; i++){
      myLayers[i].mKeyUp();
    }    
  }

}


class Bin extends Layer
{
  ArrayList data;
  PGraphics fileList;
  PImage    selectBar;
  int       selected = -1;
  int       lastClick = 0;

  //slider variables
  boolean   sliderNeeded = true;
  boolean   sliderDrag = false;
  float     sliderMin = 35;
  float     sliderMax = 160;
  float     sliderHeight = 30;
  float     sliderY = sliderMin;

  Bin()
  {
    super(43,43,300,200,0); 
    MINWIDTH = 160;
    MINHEIGHT = 100;
    RESIZEENABLED = true;
    data = new ArrayList();
    //data.add(new dataFile("C:\\Documents and Settings\\Owner\\Desktop\\processing\\data\\1.txt"));
    populateFiles();
  }

  boolean mDraw()
  {
    pg = createGraphics(w,h,JAVA2D);
    pg.beginDraw();
    pg.background(BORDERCOLOR);
    pg.fill(FILLCOLOR);
    pg.noStroke();
    pg.rect(3,3,pg.width-6,pg.height-6);

    //draw the slider if need-be
    if(sliderNeeded)
    {
      if(sliderDrag) sliderY = constrain(mouseY-y-sliderHeight/2,sliderMin,sliderMax);
      pg.strokeWeight(1);
      pg.stroke(BUTTONSTROKE);
      pg.fill(BUTTONSTROKE);
      pg.rect(10 + fileList.width+11,sliderMin,6,h-sliderMin-12);
      pg.fill(BUTTONFILL);
      pg.rect(10 + fileList.width+5,sliderY,18,sliderHeight);
    }

    sliderMax = h - 10 - sliderHeight;
    float usable = sliderMax-sliderMin;
    float pct = (sliderY-sliderMin)/usable;
    float slideTotal = max(0,fileList.height-(h-45));
    float slideAmt = slideTotal*pct;

    pg.noStroke();    
    pg.fill(BORDERCOLOR);
    pg.rect(10,35,fileList.width+4,h-35);
    pg.image(fileList,12,37-slideAmt);
    pg.image(selectBar,12,41 + selected*20 - slideAmt);
    pg.rect(10,0,fileList.width+4,37);
    pg.rect(10,h-12,fileList.width+4,12);
    pg.fill(FILLCOLOR);
    pg.rect(10,3,fileList.width+4,32);
    pg.rect(10,h-10,fileList.width+4,7);

    pg.stroke(BUTTONSTROKE);
    pg.strokeWeight(1);
    if(mouseX > x + w - 40 && mouseX < x + w - 14 && mouseY > y + 5 && mouseY < y + 31)
    {
      if(mousePressed) pg.fill(BUTTONDOWN);
      else pg.fill(BUTTONOVER);
    }
    else{
      pg.fill(BUTTONFILL); 
    }
    pg.rect(w-40,5,26,26);
    pg.image(icons[6],w-38,7,22,22);

    pg.fill(TEXTCOLOR);
    pg.textFont(font,16);
    //pg.text("Add File",w - 66, 28);
    //label  
    pg.textFont(font,20);
    pg.text("File Bin",10,28);   

    pg.endDraw();
    
    doCursor();
    
    return DONE;
  }

  void mPress()
  {
    sliderMax = h - 10 - sliderHeight;
    float usable = sliderMax-sliderMin;
    float pct = (sliderY-sliderMin)/usable;
    float slideTotal = max(0,fileList.height-(h-45));
    float slideAmt = slideTotal*pct;     
    selected = (int)(mouseY-y-37 + slideAmt)/20;
    if(selected >= data.size() || data.size() == 0) selected = -1;
    
    if(sliderNeeded && mouseX >x + 10 + fileList.width+5 && mouseX < x + fileList.width + 33 && mouseY > y+sliderY && mouseY < y+sliderY + sliderHeight){
      sliderDrag = true;
    }
    else if(mouseX > x + 12 && mouseX < x + fileList.width && mouseY > y+37 && mouseY < y+h-10 && selected != -1){
      messages.add(new Message(""+selected,ACTIVELAYER,10));
    }else if(mouseX > x + w - 70 && mouseX < x + w - 7 && mouseY > y + 10 && mouseY < y + 30)
    {
      layers.add(new FilePop(ACTIVELAYER, 0,0));
      ACTIVELAYER++;
    }else{
      dragActive = true;
      if(RESIZEENABLED) checkResize();     
    }
  }

  void mRelease()
  {
    sliderDrag = false;
    
    if(dragActive || resizeActive[0] || resizeActive[1] || resizeActive[2] || resizeActive[3]) return;
    if(mouseY > y + 37 && mouseY < y + 37 + sliderMax+sliderHeight)
    {
      if(mouseX > x + 12 && mouseX < x + 10 + fileList.width)
      {
        sliderMax = h - 10 - sliderHeight;
        float usable = sliderMax-sliderMin;
        float pct = (sliderY-sliderMin)/usable;
        float slideTotal = max(0,fileList.height-(h-45));
        float slideAmt = slideTotal*pct;     
        selected = (int)(mouseY-y-37 + slideAmt)/20;
        if(selected >= data.size() || data.size() == 0) selected = -1;
        if(selected != -1 && mouseX > x + 10 + fileList.width - 40 && mouseX < x + 10 + fileList.width - 20){
          messages.add(new Message(""+selected,ACTIVELAYER,2));
        }
        else if(selected != -1 && mouseX > x + 10 + fileList.width - 20 && mouseX < x + 10 + fileList.width){
          dataFile d = (dataFile)data.get(selected);
          layers.add(new PopConfirm(ACTIVELAYER,1,"Delete " + (d.getName()) + "?",0));
          ACTIVELAYER++;
        }
      }
    }
    else if(mouseX > x + w - 70 && mouseX < x + w - 7 && mouseY > y + 10 && mouseY < y + 30)
    {
      layers.add(new FilePop(ACTIVELAYER, 0,0));
      ACTIVELAYER++;
    }
  }

  void clear()
  {
    data = new ArrayList();
    populateFiles(); 
  }
  
  void dropFile()
  {
    data.remove(selected);
    populateFiles();
  }

  void mDrag()
  {
    super.mDrag();
    populateFiles();  
  }

  void addFile(String s)
  {
    dataFile d = new dataFile(s);
    if(d.isValid)
    {
      //see if that file is loaded already
      boolean found = false;
      for(int i = 0; i < data.size(); i++)
      {
        dataFile d2 = (dataFile)data.get(i);
        if(d2.path.equals(d.path)) found = true;
      }
      if(found){
        popWarn("File already loaded!",250);
      }
      else{
        data.add(d);
        populateFiles();               
      }
    }
    else{
      popWarn("Unrecognized filetype!");
    }    
  } 

  void populateFiles()
  {

    if(data.size()*20+10 > h-47) sliderNeeded = true;
    else sliderNeeded = false;
    
    selected = -1;  //make sure no one is selected
    fileList = createGraphics(sliderNeeded?w-40:w-26,max(h-47,(data.size())*20+10),JAVA2D);
    
    selectBar = new PImage(sliderNeeded?w-82:w-68,20);
    for(int i = 0; i < selectBar.pixels.length; i++)
    {
      selectBar.pixels[i] = color(0,0,255,48);
    }    
    
    fileList.beginDraw();
    fileList.background(FILLCOLOR);
    for(int i = 0; i < data.size(); i++)
    {
      fileList.fill(TEXTCOLOR);  
      fileList.textFont(font,16);         
      dataFile d = (dataFile)data.get(i);
      fileList.text(d.getName(),3,20*(i+1)+1);

      //now draw the properties/delete buttons
      fileList.stroke(BUTTONSTROKE);
      fileList.strokeWeight(1);
      fileList.fill(BUTTONFILL); 
      fileList.rect(fileList.width-42,20*(i)+1,20,20);            
      fileList.rect(fileList.width-22,20*(i)+1,20,20);
      fileList.image(icons[4],fileList.width-20,20*i+3,16,16);
      fileList.image(icons[2],fileList.width-40,20*i+3,16,16);
      /*
     fileList.fill(TEXTCOLOR);
       fileList.textFont(font,12);        
       fileList.line(fileList.width-20,20*(i)+3,fileList.width-4,20*(i)+19);
       fileList.line(fileList.width-4,20*(i)+3,fileList.width-20,20*(i)+19);     
       fileList.text("i",fileList.width-32,20*(i)+17);
       */
    }

    fileList.endDraw();
    fileList.modified = true;
  }
  
  int getFileNum(String whichFile)
  {
    for(int i = 0; i < data.size(); i++)
    {
      dataFile d = (dataFile)data.get(i);
      if(whichFile.equals(d.getFileName())) return i;
    }  
  return -1;  
  }
  
  dataFile getFileByNum(int whichFile)
  {
   return (dataFile)data.get(whichFile); 
  }

}

//TODO: add a close button and some global show/hide layers for the main 3
//If I add instance browsing, I'll need a slider...
class dataFileProp extends Layer   
{
  dataFile  data;
  int       lastClick = 0;

  dataFileProp(dataFile d, int _x, int _y, int _w, int _h)
  {
    super(_x,_y,_w,max(205,_h),0); 
    RESIZEENABLED = true;   
    MINWIDTH = 190;
    MINHEIGHT = 205;
    data = d;
  }

  boolean mDraw()
  {
    pg = createGraphics(w,h,JAVA2D);
    pg.beginDraw();
    pg.background(BORDERCOLOR);
    pg.fill(FILLCOLOR);
    pg.noStroke();
    pg.rect(3,3,pg.width-6,pg.height-6);
    pg.fill(TEXTCOLOR);
    pg.textFont(font,20);
    pg.text("Data File Properties",10,26);
    pg.fill(BORDERCOLOR);
    pg.rect(10,30,w-20,h-40);
    pg.fill(FILLCOLOR);
    pg.rect(12,32,w-24,h-44);

    pg.fill(TEXTCOLOR);
    pg.textFont(font,14);
    pg.textAlign(LEFT);
    pg.text("Name:",14,48);
    pg.text("File:",14,64);
    pg.text("DataPoints:",14,80);
    pg.text("Duration:",14,96);
    pg.text("Digital:",14,112);
    pg.text("Analog:",14,128);
    pg.text("Instances:",14,146);
    pg.textAlign(RIGHT);
    pg.text(data.getName(),w-14,48);
    pg.text(data.getFileName(),w-14,64);
    pg.text(data.data.length,w-14,80);
    pg.text(data.getDuration(),w-14,96);
    pg.text(data.numDigital,w-14,112);
    pg.text(data.numAnalog,w-14,128);
    pg.text(0,w-14,146);    //TODO: implement instance counts
    
    pg.stroke(BUTTONSTROKE);
    pg.fill(BUTTONFILL);
    if(mouseX > x+28 && mouseX < x+w-56  && mouseY > y+155 && mouseY < y+185)
    {
      pg.fill(BUTTONOVER);
      if(mousePressed) pg.fill(BUTTONDOWN);
    }
    pg.rect(28,155,w-56,30);
    pg.textAlign(CENTER);
    pg.fill(TEXTCOLOR);
    pg.text("PREVIEW",w/2,175);
    
    pg.endDraw();    
    
    doCursor();
    
    return DONE;    
  }


  void mRelease()
  {
    super.mRelease();
    
    if(mouseY > y + 32 && mouseY < y + 48 && mouseX > x && mouseX < x+w)
    {
      layers.add(new PopText(layers.size()-1,3,"Enter new name for " + data.getName(),0));
      ACTIVELAYER++;
    }else if(mouseX > x+28 && mouseX < x+w-56  && mouseY > y+155 && mouseY < y+185)
    {
      messages.add(new Message(true, ACTIVELAYER, 23));
    }
      
    lastClick = millis();
  }


}

class dragFile extends Layer
{
  PImage icon;
  dataFile whichFile;

  dragFile(dataFile f)
  {
    super(mouseX,mouseY,40,40,ACTIVELAYER);
    icon = loadImage("icon_joystick.png");
    whichFile = f;
    pg = createGraphics(w,h,JAVA2D);
    pg.beginDraw();
    pg.fill(255,255,255,96);
    pg.image(icon,7,8,32,32);
    pg.endDraw();
  }
  
}

//this function takes an incoming string and handles the try/catch block to check if it's an int or not
//if it's an int, it returns the value...otherwise it returns 0
int confirmInt(String s)
{
  int newInt = 0;
  try 
  {
  newInt = Integer.parseInt(s);
  } 
  catch (NumberFormatException e) {
    popWarn("You must enter an integer value!",230);
    return 0;
  }
  return newInt;
}

//ditto for floats
float confirmFloat(String s)
{
  float f = 0.0;
  try 
  {
    f = Float.parseFloat(s);
  } 
  catch (NumberFormatException e) {
    popWarn("You must enter a float value!",230);
    return 0.0;
  }
  return f;
}

/*
This tab contains two layers, the dataInstance property viewer and the in/out/scale popup.  One will be
text-based and useful for setting values numerically.  One will be graphical and used for setting in/out/scale
points.


From within the dataProp viewer (for vieing a dataInstance), the following information should be displayed:
  dataFile file;  (frozen--double click to view dataFile properites)
  String name; (edit)
  int duration; (edit)  <--editing this will change the scale
  float dataScale; (edit -- check overlap)  <--this will change the duration
  
  int numDigital; //how many are active in this instance?  (indirect edit)
  int numAnalog;  //how many are active in this instance?  (indirect edit)
  
  int inPoint; (edit -- constrain(0,outPoint-1) -- check overlap)
  int outPoint; (edit -- constrain(1,length) -- check overlap)
  
  float offset;  (edit -- check overlap)
  
  boolean[] dChan;  (edit toggle)
  boolean[] aChan;  (edit toggle)
  String[] dChanName; (edit rename)  <--not implemented yet
  String[] aChanName; (edit rename)  <--not implemented yet
  
  */


class dataProp extends Layer
{
  dataInstance data;
  int lastClick = 0;
  
  dataProp(dataInstance d,int _x, int _y, int _w, int _h)
  {
    super(_x,_y,_w,_h,0); 
    RESIZEENABLED = true;   
    MINWIDTH = 190;
    MINHEIGHT = 170;
    data = d;
  }

  boolean mDraw()
  {
    pg = createGraphics(w,h,JAVA2D);
    pg.beginDraw();
    pg.background(BORDERCOLOR);
    pg.fill(FILLCOLOR);
    pg.noStroke();
    pg.rect(3,3,pg.width-6,pg.height-6);
    pg.fill(TEXTCOLOR);
    pg.textFont(font,20);
    pg.text("dataInstance Properties",10,26);
    pg.fill(BORDERCOLOR);
    pg.rect(10,30,w-20,h-40);
    pg.fill(FILLCOLOR);
    pg.rect(12,32,w-24,h-44);
  
    pg.fill(TEXTCOLOR);
    pg.textFont(font,14);
    pg.textAlign(LEFT);
    pg.text("Name:",14,48);
    pg.text("File:",14,64);
    pg.text("Length:",14,80);
    pg.text("Scale:",14,96);
    pg.text("Offset:",14,112);
    pg.text("inPoint:",14,128);
    pg.text("outPoint:",14,144);
    pg.text("numDigital:",14,160);
    pg.text("numAnalog:",14,176);

    
    pg.textAlign(RIGHT);
    pg.text(data.name,w-14,48);
    pg.text(data.file.getName(),w-14,64);
    pg.text(data.getDuration(),w-14,80);
    pg.text(data.dataScale,w-14,96);
    pg.text((int)data.offset,w-14,112);
    pg.text(data.inPoint,w-14,128);
    pg.text(data.outPoint,w-14,144);    
    pg.text(data.numDigital,w-14,160);
    pg.text(data.numAnalog,w-14,176);

    pg.endDraw();    
    
    doCursor();
    
    return DONE;    
  }
  
  void mRelease()
  {
    if(mouseX > x+14 || mouseX < x+w-14)
    {
        int whichClick = (int)((mouseY-y-32)/16);
        if(whichClick == 0)
        {
          layers.add(new PopText(ACTIVELAYER,14,"Rename instance:",0));
          ACTIVELAYER++;
        }
        if(whichClick == 1)
        {
          messages.add(new Message(true,ACTIVELAYER,15));
        }        
        if(whichClick == 2)
        {
          layers.add(new PopText(ACTIVELAYER,16,"Enter new length:",0));
          ACTIVELAYER++;
        }
        if(whichClick == 3)
        {
          layers.add(new PopText(ACTIVELAYER,17,"Enter new scale:",0));
          ACTIVELAYER++;
        }
        if(whichClick == 4)
        {
          layers.add(new PopText(ACTIVELAYER,18,"Enter new offset:",0));
          ACTIVELAYER++;
        }
        if(whichClick == 5)
        {
          layers.add(new PopText(ACTIVELAYER,19,"Enter new inPoint:",0));
          ACTIVELAYER++;
        }
        if(whichClick == 6)
        {
          layers.add(new PopText(ACTIVELAYER,20,"Enter new outPoint:",0));
          ACTIVELAYER++;
        }        
      lastClick = millis();      
    }else{
      dragActive = false;
      Arrays.fill(resizeActive,false);
    }
  }
}
